﻿using ShadowCode.Injection.Options;
using System.CodeDom.Compiler;

namespace ShadowCode.Injection;
[Generator]
public class OptionsGenerator : Base.GeneratorBase
{
    protected override IncrementalValuesProvider<TransformData> GetCollects(IncrementalGeneratorInitializationContext context) =>
        context.SyntaxProvider.CreateSyntaxProvider(IsTarget, Transform);

    private static bool IsTarget(SyntaxNode node, CancellationToken token)
    {
        if (node is not AttributeSyntax attr) return false;
        return GetName(attr.Name) is "AddOptions" or "AddOptionsAttribute";
    }

    protected override void RegisterPostInitializationOutput(
    IncrementalGeneratorPostInitializationContext context)
    {
        context.AddSource($"ShadowCode.Injection.Options.Attribute.cs", $$"""
    /* {{version}} */
    #pragma warning disable
    namespace ShadowCode;

    internal abstract partial class AddOptionsBase : Attribute
    {
        private readonly string? sectionKey;

        public AddOptionsBase() { }

        public AddOptionsBase(string sectionKey) => this.sectionKey = sectionKey;

        public string? GetSectionKey() => sectionKey;

        /// <summary>
        /// 选项实例的名称
        /// </summary>
        public string Name { get; set; } = string.Empty;

    }

    /// <summary>
    /// 添加选项 (ShadowCode.Injection)
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    internal partial class AddOptionsAttribute : AddOptionsBase
    {
        public AddOptionsAttribute() : base() { }

        /// <summary>
        /// <inheritdoc cref="AddOptionsAttribute"/>
        /// </summary>
        /// <param name="sectionKey">config.GetSection(sectionKey)</param>
        public AddOptionsAttribute(string sectionKey) : base(sectionKey) { }

        /// <summary>
        /// (用于泛型类)选项的类型，泛型类要求类型实参
        /// </summary>
        public Type? Type { get; set; }
    }

    /// <summary>
    /// 添加选项 (ShadowCode.Injection)
    /// </summary>
    /// <typeparam name="TOptions"> (用于泛型类)选项的类型，泛型类要求类型实参</typeparam>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    internal partial class AddOptionsAttribute<TOptions> : AddOptionsBase
    {
        public AddOptionsAttribute() : base() { }

        /// <summary>
        /// <inheritdoc cref="AddOptionsAttribute"/>
        /// </summary>
        /// <param name="sectionKey">config.GetSection(sectionKey)</param>
        public AddOptionsAttribute(string sectionKey) : base(sectionKey) { }

        public Type GetOptionsType() => typeof(TOptions);
    }
    """);
    }

    protected override void AddConfigCode(SourceProductionContext context)
    { }

    protected override void Build(SourceProductionContext context,
        Compilation compilation, ImmutableArray<TransformData> array, DateTime start)
    {
        var items = array.Select(x => new OptionsAttribute(x));

        var sb = Writer(writer =>
        {
            WriteClassStart(writer);
            writer.WriteScope(() => BuildCode(writer, items, context));
        });              

        var diff = (DateTime.Now - start).TotalMilliseconds;
        sb.Insert(0, $"/* [{index}] {version} {diff:0}ms */\r\n");
        var code = sb.ToString();
        context.AddSource($"ShadowCode.Injection.Options.Extension.cs", code);
    }

    private void BuildCode(IndentedTextWriter writer,
        IEnumerable<OptionsAttribute> items,
        SourceProductionContext context)
    {
        writer.WriteLine($"static partial void g_configure({parameter})");
        writer.WriteScope(() => WriteMethodBody(writer, items, context));
    }

    private void WriteMethodBody(IndentedTextWriter writer,
        IEnumerable<OptionsAttribute> items,
        SourceProductionContext context)
    {
        foreach (var item in items)
            WriteMethodBody(writer, item, context);
    }

    private void WriteMethodBody(IndentedTextWriter writer,
        OptionsAttribute item,
        SourceProductionContext context)
    {
        try
        {
            item.Buid(writer);
        }
        catch (Error e)
        {
            context.ReportDiagnostic(e.Diagnostic);
            writer.WriteLine("#warning 生成时发生异常");
            writer.WriteLine($"/* \r\n{e.Message}\r\n */");
        }
        catch (Exception ex)
        {
            writer.WriteLine("#warning 生成时发生异常");
            writer.WriteLine($"/* \r\n{ex}\r\n */");
        }
    }
}